/**
 * 
 */
package org.starsource.forum.server.dao.jdbc;

import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;
import java.util.LinkedHashSet;
import java.util.NoSuchElementException;
import java.util.Set;

import org.springframework.dao.DataAccessException;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.jdbc.core.PreparedStatementCreator;
import org.springframework.jdbc.core.RowCallbackHandler;
import org.springframework.jdbc.support.GeneratedKeyHolder;
import org.springframework.jdbc.support.KeyHolder;

import org.starsource.forum.client.exception.DataInconsistantException;
import org.starsource.forum.client.exception.DatabaseException;
import org.starsource.forum.client.exception.LoginLogNotFoundException;
import org.starsource.forum.server.dao.LoginLogDao;
import org.starsource.forum.server.domain.LoginLog;
import org.starsource.forum.server.domain.User;

/**
 * @author james
 *
 */
public class LoginLogDaoJdbc implements LoginLogDao {

	private JdbcTemplate jdbcTemplate;

	public void setJdbcTemplate(JdbcTemplate jdbcTemplate) {
		this.jdbcTemplate = jdbcTemplate;
	}

	private static org.apache.log4j.Logger logger = org.apache.log4j.LogManager
	.getLogger(LoginLogDaoJdbc.class.getName());

	private class GroupRowCallbackHandler implements RowCallbackHandler {
		private Set<LoginLog> loginLogSet;
		private User user;
		
		public void processRow(ResultSet rs) throws SQLException {
			LoginLog loginLog = new LoginLog();
			loginLog.setId(rs.getInt("id"));
			loginLog.setIp(rs.getString("ip"));
			loginLog.setTimestamp(rs.getTimestamp("timestamp"));

			int userId = rs.getInt("user_id");
			if (user == null) {
				loginLog.setUserId(userId);
			} else if (userId == user.getId()) {
				loginLog.setUser(user);
			} else {
				throw new SQLException("Data inconsistant exception",new DataInconsistantException());
			}

			loginLogSet.add(loginLog);
		}
		
		public GroupRowCallbackHandler(Set<LoginLog> loginLogSet) {
			this.loginLogSet = loginLogSet;
		}

		public GroupRowCallbackHandler(Set<LoginLog> loginLogSet, User user) {
			this.loginLogSet = loginLogSet;
			this.user = user;
		}
	}

	/* (non-Javadoc)
	 * @see org.starsource.forum.server.dao.LoginLogDao#delete(org.starsource.forum.server.domain.LoginLog)
	 */
	@Override
	public void delete(final LoginLog loginLog) throws DatabaseException {
		try {
			final String sql = "DELETE FROM t_login_log WHERE id = ?";
			jdbcTemplate.update(new PreparedStatementCreator() {
				public PreparedStatement createPreparedStatement(Connection conn) throws SQLException {
					PreparedStatement ps = conn.prepareStatement(sql);
					ps.setInt(1, loginLog.getId());
					return ps;
				}
			});
		} catch (DataAccessException e) {
			logger.error(e.toString());
			throw new DatabaseException();
		}
	}

	/* (non-Javadoc)
	 * @see org.starsource.forum.server.dao.LoginLogDao#getLastLoginLog(org.starsource.forum.server.domain.User)
	 */
	@Override
	public LoginLog getLastLoginLog(User user)
			throws LoginLogNotFoundException, DatabaseException {
		try {
			String sql = "SELECT * FROM t_login_log WHERE user_id = ? ORDER BY timestamp DESC";
			final Set<LoginLog> loginLogSet = new LinkedHashSet<LoginLog>();
			jdbcTemplate.query(sql, new Object[] {user.getId()}, new GroupRowCallbackHandler(loginLogSet, user));
			return loginLogSet.iterator().next();
		} catch (DataAccessException e) {
			logger.error(e.toString());
			throw new DatabaseException();
		} catch (NoSuchElementException e) {
			logger.error(e.toString());
			throw new LoginLogNotFoundException();
		}
	}

	/* (non-Javadoc)
	 * @see org.starsource.forum.server.dao.LoginLogDao#getLoginLog(int)
	 */
	@Override
	public LoginLog getLoginLog(int id) throws LoginLogNotFoundException,
			DatabaseException {
		try {
			String sql = "SELECT * FROM t_login_log WHERE id = ?";
			final Set<LoginLog> loginLogSet = new LinkedHashSet<LoginLog>();
			jdbcTemplate.query(sql, new Object[] {id}, new GroupRowCallbackHandler(loginLogSet));
			return loginLogSet.iterator().next();
		} catch (DataAccessException e) {
			logger.error(e.toString());
			throw new DatabaseException();
		} catch (NoSuchElementException e) {
			logger.error(e.toString());
			throw new LoginLogNotFoundException();
		}
	}

	/* (non-Javadoc)
	 * @see org.starsource.forum.server.dao.LoginLogDao#save(org.starsource.forum.server.domain.LoginLog)
	 */
	@Override
	public void save(final LoginLog loginLog) throws DatabaseException {
		final String sql = "INSERT INTO t_login_log(user_id, ip, timestamp) VALUES(?,?,?)";
		KeyHolder keyHolder = new GeneratedKeyHolder();
		jdbcTemplate.update(new PreparedStatementCreator() {
			public PreparedStatement createPreparedStatement(Connection conn) throws SQLException {
				PreparedStatement ps = conn.prepareStatement(sql, Statement.RETURN_GENERATED_KEYS); 
				
				if (loginLog.getUserId() == 0) {
					ps.setNull(1, java.sql.Types.INTEGER);					
				} else {
					ps.setInt(1, loginLog.getUserId());
				}
				ps.setString(2, loginLog.getIp());
				ps.setTimestamp(3, loginLog.getTimestamp());

				return ps;
			}
		}, keyHolder);
		loginLog.setId(keyHolder.getKey().intValue());
	}

	/* (non-Javadoc)
	 * @see org.starsource.forum.server.dao.LoginLogDao#update(org.starsource.forum.server.domain.LoginLog)
	 */
	@Override
	public void update(final LoginLog loginLog) throws DatabaseException {
		final String sql = "UPDATE t_login_log set user_id = ?, ip = ?, timestamp = ?  WHERE id = ?";
		jdbcTemplate.update(new PreparedStatementCreator() {
			public PreparedStatement createPreparedStatement(Connection conn) throws SQLException {
				PreparedStatement ps = conn.prepareStatement(sql);
				
				ps.setInt(1, loginLog.getUserId());
				ps.setString(2, loginLog.getIp());
				ps.setTimestamp(3, loginLog.getTimestamp());
				ps.setInt(4, loginLog.getId());
				return ps;
			}
		});
	}

}
